#ifndef __GLF_CORE_READWRITEMUTEXLOCK_H_INCLUDED__
#define __GLF_CORE_READWRITEMUTEXLOCK_H_INCLUDED__

#include <glf/config.h>
#include <glf/core/threadTraits.h>
#include <glf/core/mutex.h>
#include <glf/core/condition.h>
#include <glf/core/inlines.h>

namespace glf
{

class ReadWriteMutexLock : private NonCopyable
{
public:
	ReadWriteMutexLock();
	~ReadWriteMutexLock();

	// (Get Shared Read Lock) attempts to acquire a shared read lock on the
	// read/write lock specified by rwlock.
	int readLock(unsigned int time = 0);

	// (Get Exclusive Write Lock) attempts to acquire an exclusive write lock
	// on the read/write lock specified by rwlock.
	int writeLock(unsigned int time = 0);

	// (Get Shared Read Lock with No Wait) attempts to acquire a shared read
	// lock on the read/write lock specified by rwlock.
	int tryReadLock();

	// (Get Exclusive Write Lock with No Wait) attempts to acquire an exclusive
	// write lock on the read/write lock specified by rwlock.
	int tryWriteLock();

	// (Unlock Shared Read Lock) unlocks a shared read held by the calling thread.
	int readUnlock();

	// (Unlock Exclusive Write) unlocks exclusive write lock held by the calling thread.
	int writeUnlock();

protected:
	Mutex CountersMutex;
	Condition CanReadCondition;
	Condition CanWriteCondition;
	int Readers;
	int Writers;
	ThreadId WriteAccessOwner;
};

class ScopedReadMutexLock : private NonCopyable
{
public:
	ScopedReadMutexLock(ReadWriteMutexLock &lock)
		: Lock(lock)
	{
		Lock.readLock();
	}

	~ScopedReadMutexLock()
	{
		Lock.readUnlock();
	}

	ReadWriteMutexLock &Lock;
};

class ScopedWriteMutexLock : private NonCopyable
{
public:
	ScopedWriteMutexLock(ReadWriteMutexLock &lock)
		: Lock(lock) {
		Lock.writeLock();
	}

	~ScopedWriteMutexLock() {
		Lock.writeUnlock();
	}

	ReadWriteMutexLock &Lock;
};

class PromoteScopedWriteMutexLock : private NonCopyable
{
public:
	PromoteScopedWriteMutexLock(ScopedReadMutexLock& scopedLock)
		: Lock(scopedLock.Lock) {
		Lock.readUnlock();
		Lock.writeLock();
	}

	~PromoteScopedWriteMutexLock() {
		Lock.writeUnlock();
		Lock.readLock();
	}

	ReadWriteMutexLock& Lock;
};

} // namespace glf

#endif //__GLF_CORE_READWRITEMUTEXLOCK_H_INCLUDED__
